﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Daphne.GameShared;

namespace GameEngine.PDNParser
{
    /// <summary>
    /// The list of syntax symbols
    /// </summary>
    public enum enumSyntaxSymbol
    {
        // terminal symbols
        MoveNumber, MoveSeparator, CaptureSeparator, AlphaSquare, NumSquare, MoveStrength, Nag,
        LParen, RParen, LBracket, RBracket, Asterix, Setup, String, Comment, Identifier, 
        Win1, Draw1, Loss1, Win2, Draw2, Loss2, DoubleForfeit, Ellipses,

        // non-terminal symbols
        PDNFILE, PDNFILE_C, GAME, GAMEHEADER, GAMEHEADER_C, PDNTAG, GAMEBODY, GAMEBODY_C ,
        GAMEBODYITEM, VARIATION, GAMEMOVE, GAMEMOVE_C, GAMEMOVE_C2, NORMALMOVE, CAPTUREMOVE, CAPTUREMOVE_C, 
        POSITION, RESULT, RESULT1, RESULT2,


        // pop-stack symbol
        PopStack,

        // other symbol or end of file
        Eof
    };

    
    /// <summary>
    /// PDN Syntax parsing rule
    /// </summary>
    public class PDNParserRule
    {
        public enumSyntaxSymbol StartingToken
        { get; set; }

        public List<enumSyntaxSymbol> Rule
        { get; set; }

        public List<enumLexToken> FirstCatchSymbols
        { get; set; }

        public List<enumLexToken> SecondCatchSymbols
        { get; set; }

        public bool PopStack
        { get; set; }


        public PDNParserRule()
        {
            Rule = new List<enumSyntaxSymbol>();
            FirstCatchSymbols = new List<enumLexToken>();
            SecondCatchSymbols = new List<enumLexToken>();
            PopStack = false;
        }
    }

    /// <summary>
    /// Syntax tree parser for PDN, builds the syntax tree
    /// </summary>
    public class PDNSyntaxParser
    {
        /// <summary>
        /// Rules for syntax parsing
        /// </summary>
        private List<PDNParserRule> lstRules;
        private LexicalStream _stream;
        private Stack<object> _SyntaxTree;
        private object _lastPopedItem;

        private Stack<enumSyntaxSymbol> _SyntaxStack;
        private ILocalizer _Localizer;


        /// <summary>
        /// Construction of rules
        /// </summary>
        public PDNSyntaxParser(ILocalizer localizer)
        {
            lstRules = new List<PDNParserRule>();
            _stream = null;
            _SyntaxTree = new Stack<object>();
            _lastPopedItem = null;
            _SyntaxStack = new Stack<enumSyntaxSymbol>();
            _Localizer = localizer;

            // build the rules
            BuildRuleList();
        }

        /// <summary>
        /// Resulting error message
        /// </summary>
        public string ErrorMessage
        { get; set; }

        /// <summary>
        /// The main PDN syntax tree parsing method
        /// </summary>
        public bool Parse(List<LexToken> lstLexTokens, out PDNRawGameList gameList)
        {
            gameList = null;

            // init parser
            _stream = new LexicalStream(lstLexTokens);
            
            _SyntaxStack.Push(enumSyntaxSymbol.Eof);
            _SyntaxStack.Push(enumSyntaxSymbol.PDNFILE);

            // the main syntax loop
            while (_SyntaxStack.Count != 0)
            {
                // should pop syntax tree stack
                if (_SyntaxStack.Peek() == enumSyntaxSymbol.PopStack)
                {
                    PopSyntaxStack();
                    _SyntaxStack.Pop();
                    continue;
                }

                // peek one lexical token
                enumLexToken lexToken = _stream.PeekStream(1)[0];
                if (CompareLeximalSymbols(_SyntaxStack.Peek(), lexToken) == true)
                {
                    // if we are processing lexical token = terminal symbol
                    enumSyntaxSymbol ssSymbol = _SyntaxStack.Pop();
                    
                    // if finish, exit
                    if (lexToken == enumLexToken.EOF)
                        break;
                    
                    // process terminal symbol
                    ProcessTerminalSymbol(ssSymbol, _stream.Read());                   
                }
                else
                {
                    // if we are processing non-terminal symbol
                    enumSyntaxSymbol ssSymbol = _SyntaxStack.Pop();

                    PDNParserRule rule = FindParsingRule(ssSymbol, _stream);
                    if (rule != null)
                    {
                        // process non terminal symbol
                        ProcessNonTerminalSymbol(ssSymbol);

                        // push onto the stack the rule
                        if (rule.PopStack == true)
                            _SyntaxStack.Push(enumSyntaxSymbol.PopStack);
                        for (int i = rule.Rule.Count - 1; i >= 0; i--)
                            _SyntaxStack.Push(rule.Rule[i]);
                    }
                    else
                    {
                        LexToken token = _stream.Read();
                        // parsing error
                        string sSymbolText = _Localizer.GetStringByKey(GetLexTokenName(lexToken));
                        this.ErrorMessage = _Localizer.FormatMessage("PDNUnexpectedToken: "+ lexToken.ToString() + ", Line: " + token.lLineNumber);
                        return false;
                    }
                }
            } // while


            gameList = _lastPopedItem as PDNRawGameList;
            return true;
        }

        #region Localizing methods

        private string GetLexTokenName(enumLexToken lexToken)
        {
            switch (lexToken)
            {
                case enumLexToken.AlphaSquare:
                    return "PDNTokenAlphaSquare";
                case enumLexToken.Asterix:
                    return "PDNTokenAsterix";
                case enumLexToken.CaptureSeparator:
                    return "PDNTokenCaptureSeparator";
                case enumLexToken.Comment:
                    return "PDNTokenComment";
                case enumLexToken.EOF:
                    return "PDNTokenEOF";
                case enumLexToken.Identifier:
                    return "PDNTokenIdentifier";
                case enumLexToken.LBracket:
                    return "PDNTokenLBracket";
                case enumLexToken.LParen:
                    return "PDNTokenLParen";
                case enumLexToken.MoveNumber:
                    return "PDNTokenMoveNumber";
                case enumLexToken.MoveSeparator:
                    return "PDNTokenMoveSeparator";
                case enumLexToken.MoveStrength:
                    return "PDNTokenMoveStrength";
                case enumLexToken.Nag:
                    return "PDNTokenNag";
                case enumLexToken.NumSquare:
                    return "PDNTokenNumSquare";
                case enumLexToken.RBracket:
                    return "PDNTokenRBracket";
                case enumLexToken.RParen:
                    return "PDNTokenRParen";
                case enumLexToken.Setup:
                    return "PDNTokenSetup";
                case enumLexToken.String:
                    return "PDNTokenString";
                default:
                    return "PDNTokenUknown";
            }
        }

        #endregion

        #region Parsing Methods

        private PDNParserRule FindParsingRule(enumSyntaxSymbol ssSymbol, LexicalStream _stream)
        {
            // get all rules for this syntax symbol
            List<PDNParserRule> ruleList = lstRules.Where(r => r.StartingToken == ssSymbol).ToList();

            // no rule for this syntax symbol
            if (ruleList.Count == 0)
                return null;

            List<enumLexToken> lexTokenList = _stream.PeekStream();

            // check first catch symbol
            List<PDNParserRule> ruleList1 = ruleList
                .Where(r => r.FirstCatchSymbols.Contains(lexTokenList[0]))
                .ToList();
            if (ruleList1.Count == 0)
            {
                ruleList1 = ruleList
                    .Where(r => r.FirstCatchSymbols.Contains(enumLexToken.OtherSymbol))
                    .ToList();
            }
            // if no rule for this catch symbol?
            if (ruleList1.Count == 0)
                return null;

            // if just one rule found for this symbol
            if (ruleList1.Count == 1)
                return ruleList1[0];

            // check the second catch symbol if required
            ruleList1 = ruleList1
                        .Where(r => r.SecondCatchSymbols.Contains(lexTokenList[1]))
                        .ToList();

            if (ruleList1.Count == 0)
                return null;

            return ruleList1[0];            
        }
        
        
        /// <summary>
        /// Compares lexical token to the terminal syntax symbol
        /// </summary>
        private bool CompareLeximalSymbols(enumSyntaxSymbol syntax, enumLexToken token)
        {
            switch (token)
            {
                case enumLexToken.AlphaSquare:
                    if (syntax == enumSyntaxSymbol.AlphaSquare) return true;
                    break;
                case enumLexToken.Asterix:
                    if (syntax == enumSyntaxSymbol.Asterix) return true;
                    break;
                case enumLexToken.CaptureSeparator:
                    if (syntax == enumSyntaxSymbol.CaptureSeparator) return true;
                    break;
                case enumLexToken.Comment:
                    if (syntax == enumSyntaxSymbol.Comment) return true;
                    break;
                case enumLexToken.EOF:
                    if (syntax == enumSyntaxSymbol.Eof) return true;
                    break;
                case enumLexToken.Identifier:
                    if (syntax == enumSyntaxSymbol.Identifier) return true;
                    break;
                case enumLexToken.LBracket:
                    if (syntax == enumSyntaxSymbol.LBracket) return true;
                    break;
                case enumLexToken.LParen:
                    if (syntax == enumSyntaxSymbol.LParen) return true;
                    break;
                case enumLexToken.MoveNumber:
                    if (syntax == enumSyntaxSymbol.MoveNumber) return true;
                    break;
                case enumLexToken.MoveSeparator:
                    if (syntax == enumSyntaxSymbol.MoveSeparator) return true;
                    break;
                case enumLexToken.MoveStrength:
                    if (syntax == enumSyntaxSymbol.MoveStrength) return true;
                    break;
                case enumLexToken.Nag:
                    if (syntax == enumSyntaxSymbol.Nag) return true;
                    break;
                case enumLexToken.NumSquare:
                    if (syntax == enumSyntaxSymbol.NumSquare) return true;
                    break;
                case enumLexToken.RBracket:
                    if (syntax == enumSyntaxSymbol.RBracket) return true;
                    break;
                case enumLexToken.RParen:
                    if (syntax == enumSyntaxSymbol.RParen) return true;
                    break;
                case enumLexToken.Setup:
                    if (syntax == enumSyntaxSymbol.Setup) return true;
                    break;
                case enumLexToken.String:
                    if (syntax == enumSyntaxSymbol.String) return true;
                    break;
                case enumLexToken.DoubleForfeit:
                    if (syntax == enumSyntaxSymbol.DoubleForfeit) return true;
                    break;
                case enumLexToken.Win1:
                    if (syntax == enumSyntaxSymbol.Win1) return true;
                    break;
                case enumLexToken.Draw1:
                    if (syntax == enumSyntaxSymbol.Draw1) return true;
                    break;
                case enumLexToken.Loss1:
                    if (syntax == enumSyntaxSymbol.Loss1) return true;
                    break;
                case enumLexToken.Win2:
                    if (syntax == enumSyntaxSymbol.Win2) return true;
                    break;
                case enumLexToken.Draw2:
                    if (syntax == enumSyntaxSymbol.Draw2) return true;
                    break;
                case enumLexToken.Loss2:
                    if (syntax == enumSyntaxSymbol.Loss2) return true;
                    break;
                case enumLexToken.Ellipses:
                    if (syntax == enumSyntaxSymbol.Ellipses) return true;
                    break;

                default:
                    return false;
            }

            return false;
        }



        #endregion

        #region Processing methods

        /// <summary>
        /// Processes terminal symbol
        /// </summary>
        private void ProcessTerminalSymbol(enumSyntaxSymbol eSymbol, LexToken token)
        {
            object objParent = _SyntaxTree.Pop();

            switch (eSymbol)
            {
                case enumSyntaxSymbol.Identifier:
                    {
                        Debug.Assert(objParent is PDNRawHeader);
                        PDNRawHeader header = objParent as PDNRawHeader;
                        header.Key = token.sValue;
                    }
                    break;
                case enumSyntaxSymbol.String:
                    {
                        Debug.Assert(objParent is PDNRawHeader);
                        PDNRawHeader header = objParent as PDNRawHeader;
                        header.Value = token.sValue;
                    }
                    break;
                case enumSyntaxSymbol.Comment:
                    {
                        Debug.Assert(objParent is PDNRawGameBody);
                        PDNRawGameBody body = objParent as PDNRawGameBody;
                        PDNRawComment comment = new PDNRawComment();
                        comment.Comment = token.sValue;
                        body.Items.Add(comment);
                    }
                    break;
                case enumSyntaxSymbol.Setup:
                    {
                        Debug.Assert(objParent is PDNRawGameBody);
                        PDNRawGameBody body = objParent as PDNRawGameBody;
                        PDNRawFENSetupString setup = new PDNRawFENSetupString();
                        setup.FENSetup = token.sValue;
                        body.Items.Add(setup);
                    }
                    break;
                case enumSyntaxSymbol.Nag:
                    {
                        Debug.Assert(objParent is PDNRawGameBody);
                        PDNRawGameBody body = objParent as PDNRawGameBody;
                        PDNRawNAG nag = new PDNRawNAG();
                        nag.NagValue = token.sValue;
                        body.Items.Add(nag);
                    }
                    break;
                case enumSyntaxSymbol.MoveNumber:
                    {
                        Debug.Assert(objParent is PDNRawMove);
                        PDNRawMove move = objParent as PDNRawMove;
                        move.MoveNumber = Convert.ToInt32(token.sValue.Substring(0, token.sValue.Length - 1));
                    }
                    break;

                case enumSyntaxSymbol.MoveSeparator:
                    {
                        Debug.Assert(objParent is PDNRawMove);
                        PDNRawMove move = objParent as PDNRawMove;
                        move.CaptureMove = false;
                    }
                    break;
                case enumSyntaxSymbol.Ellipses:
                    {
                        Debug.Assert(objParent is PDNRawMove);
                        PDNRawMove move = objParent as PDNRawMove;
                        move.CaptureMove = false;
                        move.MoveStrength = string.Empty;
                        move.EmptyMove = true;
                    }
                    break;

                case enumSyntaxSymbol.CaptureSeparator:
                    {
                        Debug.Assert(objParent is PDNRawMove);
                        PDNRawMove move = objParent as PDNRawMove;
                        move.CaptureMove = true;
                    }
                    break;
                case enumSyntaxSymbol.MoveStrength:
                    {
                        Debug.Assert(objParent is PDNRawMove);
                        PDNRawMove move = objParent as PDNRawMove;
                        move.MoveStrength = token.sValue;
                    }
                    break;
                case enumSyntaxSymbol.AlphaSquare:
                    {
                        Debug.Assert(objParent is PDNRawMove);
                        PDNRawMove move = objParent as PDNRawMove;
                        PDNRawPosition pos = new PDNRawPosition();
                        pos.PositionType = enumPDNPositionType.Chess;
                        pos.Position = token.sValue;
                        move.Positions.Add(pos);
                    }
                    break;
                case enumSyntaxSymbol.NumSquare:
                    {
                        Debug.Assert(objParent is PDNRawMove);
                        PDNRawMove move = objParent as PDNRawMove;
                        PDNRawPosition pos = new PDNRawPosition();
                        pos.PositionType = enumPDNPositionType.Checkers;
                        pos.Position = token.sValue;
                        move.Positions.Add(pos);
                    }
                    break;
                case enumSyntaxSymbol.Win1:
                case enumSyntaxSymbol.Loss1:
                case enumSyntaxSymbol.Draw1:
                case enumSyntaxSymbol.Win2:
                case enumSyntaxSymbol.Loss2:
                case enumSyntaxSymbol.Draw2:
                case enumSyntaxSymbol.DoubleForfeit:
                    {
                        if(_lastPopedItem is PDNRawGame)
                        {
                            PDNRawGame rawGame = _lastPopedItem as PDNRawGame;
                            switch(eSymbol)
                            {
                                case enumSyntaxSymbol.Win1: rawGame.GameResult = enumPDNGameResult.Win1; break;
                                case enumSyntaxSymbol.Loss1: rawGame.GameResult = enumPDNGameResult.Loss1; break;
                                case enumSyntaxSymbol.Draw1: rawGame.GameResult = enumPDNGameResult.Draw1; break;
                                case enumSyntaxSymbol.Win2: rawGame.GameResult = enumPDNGameResult.Win2; break;
                                case enumSyntaxSymbol.Loss2: rawGame.GameResult = enumPDNGameResult.Loss2; break;
                                case enumSyntaxSymbol.Draw2: rawGame.GameResult = enumPDNGameResult.Draw2; break;
                                case enumSyntaxSymbol.DoubleForfeit: rawGame.GameResult = enumPDNGameResult.DoubleForfeit; break;
                            }
                        }
                    }
                    break;
            } // switch

            _SyntaxTree.Push(objParent);
        }

        /// <summary>
        /// Processes non-terminal symbol
        /// </summary>
        private void ProcessNonTerminalSymbol(enumSyntaxSymbol eSymbol)
        {
            // pop the parent object, if exists
            object objParent = null;
            if (_SyntaxTree.Count != 0)
                objParent = _SyntaxTree.Pop();

            switch (eSymbol)
            {
                case enumSyntaxSymbol.PDNFILE:
                    PDNRawGameList gameList = new PDNRawGameList();
                    _SyntaxTree.Push(gameList);
                    break;
                case enumSyntaxSymbol.GAME:
                    Debug.Assert(objParent is PDNRawGameList);
                    PDNRawGame game = new PDNRawGame();
                    ((PDNRawGameList)objParent).Games.Add(game);
                    _SyntaxTree.Push(objParent);
                    _SyntaxTree.Push(game);
                    break;
                case enumSyntaxSymbol.GAMEBODY:
                    Debug.Assert(objParent is PDNRawGame || objParent is PDNRawGameBody);
                    PDNRawGameBody body = new PDNRawGameBody();
                    if (objParent is PDNRawGame)
                        ((PDNRawGame)objParent).GameBody = body;
                    else if (objParent is PDNRawGameBody)
                        ((PDNRawGameBody)objParent).Items.Add(body);
                    _SyntaxTree.Push(objParent);
                    _SyntaxTree.Push(body);
                    break;
                case enumSyntaxSymbol.GAMEMOVE:
                    Debug.Assert(objParent is PDNRawGameBody);
                    PDNRawMove move = new PDNRawMove();
                    ((PDNRawGameBody)objParent).Items.Add(move);
                    _SyntaxTree.Push(objParent);
                    _SyntaxTree.Push(move);
                    break;
                case enumSyntaxSymbol.PDNTAG:
                    Debug.Assert(objParent is PDNRawGame);
                    PDNRawHeader header = new PDNRawHeader();
                    ((PDNRawGame)objParent).Headers.Add(header);
                    _SyntaxTree.Push(objParent);
                    _SyntaxTree.Push(header);
                    break;
                default:
                    if (objParent != null)
                        _SyntaxTree.Push(objParent);
                    break;
            }

        }

        /// <summary>
        /// Removes the first object on syntax tree stack
        /// </summary>
        private void PopSyntaxStack()
        {
            _lastPopedItem = _SyntaxTree.Pop();
        }

        #endregion

        #region Parsing Rules

        /// <summary>
        /// Builds the rule list for syntax parsing of PDN file
        /// </summary>
        private void BuildRuleList()
        {
            // PDNFILE 
            AddParsingRule(enumSyntaxSymbol.PDNFILE, 
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.GAME, enumSyntaxSymbol.PDNFILE_C },
                        new enumLexToken[] { enumLexToken.MoveNumber, enumLexToken.AlphaSquare, enumLexToken.NumSquare,
                                            enumLexToken.LParen, enumLexToken.Comment, enumLexToken.Setup, enumLexToken.Nag,
                                            enumLexToken.LBracket },
                        new enumLexToken[] {},
                        true);
            // PDNFILE_C 
            AddParsingRule(enumSyntaxSymbol.PDNFILE_C,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.Asterix, enumSyntaxSymbol.GAME, enumSyntaxSymbol.PDNFILE_C},
                        new enumLexToken[] {enumLexToken.Asterix},
                        new enumLexToken[] {enumLexToken.LBracket, enumLexToken.MoveNumber, enumLexToken.AlphaSquare,
                                        enumLexToken.NumSquare, enumLexToken.LParen, enumLexToken.Comment,
                                        enumLexToken.Setup, enumLexToken.Nag},
                        false);
            AddParsingRule(enumSyntaxSymbol.PDNFILE_C,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.Asterix},
                        new enumLexToken[] {enumLexToken.Asterix},
                        new enumLexToken[] {enumLexToken.EOF },
                        false);

            AddParsingRule(enumSyntaxSymbol.PDNFILE_C,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.RESULT, enumSyntaxSymbol.GAME, enumSyntaxSymbol.PDNFILE_C },
                        new enumLexToken[] { enumLexToken.Win1, enumLexToken.Draw1, enumLexToken.Loss1,
                                              enumLexToken.Win2, enumLexToken.Draw2, enumLexToken.Loss2,
                                                enumLexToken.DoubleForfeit },
                        new enumLexToken[] {enumLexToken.LBracket, enumLexToken.MoveNumber, enumLexToken.AlphaSquare,
                                        enumLexToken.NumSquare, enumLexToken.LParen, enumLexToken.Comment,
                                        enumLexToken.Setup, enumLexToken.Nag },
                        false);

            AddParsingRule(enumSyntaxSymbol.PDNFILE_C,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.RESULT},
                        new enumLexToken[] { enumLexToken.Win1, enumLexToken.Draw1, enumLexToken.Loss1,
                                              enumLexToken.Win2, enumLexToken.Draw2, enumLexToken.Loss2,
                                                enumLexToken.DoubleForfeit },
                        new enumLexToken[] { enumLexToken.EOF},
                        false);

            AddParsingRule(enumSyntaxSymbol.PDNFILE_C,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.RESULT },
                        new enumLexToken[] { enumLexToken.Win1, enumLexToken.Draw1, enumLexToken.Loss1,
                                              enumLexToken.Win2, enumLexToken.Draw2, enumLexToken.Loss2,
                                                enumLexToken.DoubleForfeit },
                        new enumLexToken[] { enumLexToken.Asterix },
                        false);

            AddParsingRule(enumSyntaxSymbol.PDNFILE_C,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.GAME, enumSyntaxSymbol.PDNFILE_C },
                        new enumLexToken[] { enumLexToken.LBracket },
                        new enumLexToken[] {},
                        false);



            AddParsingRule(enumSyntaxSymbol.PDNFILE_C,
                        new enumSyntaxSymbol[] { },
                        new enumLexToken[] {enumLexToken.EOF},
                        new enumLexToken[] {},
                        false);

            AddParsingRule(enumSyntaxSymbol.RESULT,
                            new enumSyntaxSymbol[] {enumSyntaxSymbol.RESULT1},
                            new enumLexToken[] { enumLexToken.Win1, enumLexToken.Draw1, enumLexToken.Loss1 },
                            new enumLexToken[] {},
                            false);

            AddParsingRule(enumSyntaxSymbol.RESULT,
                            new enumSyntaxSymbol[] {enumSyntaxSymbol.RESULT2},
                            new enumLexToken[] { enumLexToken.Win2, enumLexToken.Draw2, enumLexToken.Loss2 },
                            new enumLexToken[] {},
                            false);

            AddParsingRule(enumSyntaxSymbol.RESULT,
                            new enumSyntaxSymbol[] {enumSyntaxSymbol.DoubleForfeit },
                            new enumLexToken[] { enumLexToken.DoubleForfeit },
                            new enumLexToken[] {},
                            false);

            AddParsingRule(enumSyntaxSymbol.RESULT1,
                            new enumSyntaxSymbol[] { enumSyntaxSymbol.Win1 },
                            new enumLexToken[] { enumLexToken.Win1 },
                            new enumLexToken[] {},
                            false);

            AddParsingRule(enumSyntaxSymbol.RESULT1,
                            new enumSyntaxSymbol[] { enumSyntaxSymbol.Draw1 },
                            new enumLexToken[] { enumLexToken.Draw1 },
                            new enumLexToken[] {},
                            false);

            AddParsingRule(enumSyntaxSymbol.RESULT1,
                            new enumSyntaxSymbol[] { enumSyntaxSymbol.Loss1 },
                            new enumLexToken[] { enumLexToken.Loss1 },
                            new enumLexToken[] {},
                            false);

            AddParsingRule(enumSyntaxSymbol.RESULT2,
                            new enumSyntaxSymbol[] { enumSyntaxSymbol.Win2 },
                            new enumLexToken[] { enumLexToken.Win2 },
                            new enumLexToken[] {},
                            false);

            AddParsingRule(enumSyntaxSymbol.RESULT2,
                            new enumSyntaxSymbol[] { enumSyntaxSymbol.Draw2 },
                            new enumLexToken[] { enumLexToken.Draw2 },
                            new enumLexToken[] {},
                            false);

            AddParsingRule(enumSyntaxSymbol.RESULT2,
                            new enumSyntaxSymbol[] { enumSyntaxSymbol.Loss2 },
                            new enumLexToken[] { enumLexToken.Loss2 },
                            new enumLexToken[] {},
                            false);

            // GAME 
            AddParsingRule(enumSyntaxSymbol.GAME,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.GAMEHEADER, enumSyntaxSymbol.GAMEBODY},
                        new enumLexToken[] {enumLexToken.LBracket},
                        new enumLexToken[] {},
                        true);

            AddParsingRule(enumSyntaxSymbol.GAME,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.GAMEBODY},
                        new enumLexToken[] {enumLexToken.MoveNumber, enumLexToken.AlphaSquare, enumLexToken.NumSquare,
                                            enumLexToken.LParen, enumLexToken.Comment, enumLexToken.Setup, enumLexToken.Nag},
                        new enumLexToken[] {},
                        true);
            // GAMEHEADER 
            AddParsingRule(enumSyntaxSymbol.GAMEHEADER,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.PDNTAG, enumSyntaxSymbol.GAMEHEADER_C},
                        new enumLexToken[] {enumLexToken.LBracket},
                        new enumLexToken[] {},
                        false);
            // GAMEHEADER_C
            AddParsingRule(enumSyntaxSymbol.GAMEHEADER_C,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.PDNTAG, enumSyntaxSymbol.GAMEHEADER_C},
                        new enumLexToken[] {enumLexToken.LBracket},
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.GAMEHEADER_C,
                        new enumSyntaxSymbol[] {},
                        new enumLexToken[] {enumLexToken.OtherSymbol},
                        new enumLexToken[] {},
                        false);
            // PDNTAG 
            AddParsingRule(enumSyntaxSymbol.PDNTAG,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.LBracket, enumSyntaxSymbol.Identifier, enumSyntaxSymbol.String, enumSyntaxSymbol.RBracket},
                        new enumLexToken[] {enumLexToken.LBracket},
                        new enumLexToken[] {},
                        true);
            // GAMEBODY 
            AddParsingRule(enumSyntaxSymbol.GAMEBODY,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.GAMEBODYITEM, enumSyntaxSymbol.GAMEBODY_C},
                        new enumLexToken[] {enumLexToken.MoveNumber, enumLexToken.AlphaSquare, enumLexToken.NumSquare,
                                            enumLexToken.LParen, enumLexToken.Comment, enumLexToken.Setup, enumLexToken.Nag},
                        new enumLexToken[] {},
                        true);
            // GAMEBODY 
            AddParsingRule(enumSyntaxSymbol.GAMEBODY,
                        new enumSyntaxSymbol[] { },
                        new enumLexToken[] { enumLexToken.Asterix, enumLexToken.Win1, enumLexToken.Draw1, enumLexToken.Loss1,
                                              enumLexToken.Win2, enumLexToken.Draw2, enumLexToken.Loss2,
                                                enumLexToken.DoubleForfeit, enumLexToken.EOF },
                        new enumLexToken[] { },
                        true);


            // GAMEBODY_C 
            AddParsingRule(enumSyntaxSymbol.GAMEBODY_C,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.GAMEBODYITEM, enumSyntaxSymbol.GAMEBODY_C},
                        new enumLexToken[] {enumLexToken.MoveNumber, enumLexToken.AlphaSquare, enumLexToken.NumSquare,
                                            enumLexToken.LParen, enumLexToken.Comment, enumLexToken.Setup, enumLexToken.Nag},
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.GAMEBODY_C,
                        new enumSyntaxSymbol[] {},
                        new enumLexToken[] {enumLexToken.OtherSymbol},
                        new enumLexToken[] {},
                        false);
            // GAMEBODYITEM 
            AddParsingRule(enumSyntaxSymbol.GAMEBODYITEM,
                        new enumSyntaxSymbol[] {enumSyntaxSymbol.GAMEMOVE},
                        new enumLexToken[] {enumLexToken.MoveNumber, enumLexToken.NumSquare, enumLexToken.AlphaSquare},
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.GAMEBODYITEM,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.VARIATION},
                        new enumLexToken[] { enumLexToken.LParen},
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.GAMEBODYITEM,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.Comment},
                        new enumLexToken[] { enumLexToken.Comment },
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.GAMEBODYITEM,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.Setup},
                        new enumLexToken[] { enumLexToken.Setup },
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.GAMEBODYITEM,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.Nag},
                        new enumLexToken[] { enumLexToken.Nag},
                        new enumLexToken[] {},
                        false);
            // VARIATION 
            AddParsingRule(enumSyntaxSymbol.VARIATION,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.LParen, enumSyntaxSymbol.GAMEBODY, enumSyntaxSymbol.RParen},
                        new enumLexToken[] { enumLexToken.LParen },
                        new enumLexToken[] {},
                        false);
            // GAMEMOVE 
            AddParsingRule(enumSyntaxSymbol.GAMEMOVE,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.MoveNumber, enumSyntaxSymbol.GAMEMOVE_C },
                        new enumLexToken[] { enumLexToken.MoveNumber },
                        new enumLexToken[] { enumLexToken.AlphaSquare, enumLexToken.NumSquare, enumLexToken.Ellipses },
                        true);
            
            // Special rule for incompatibility of PDN formats of Czech Federation of Checkers
            AddParsingRule(enumSyntaxSymbol.GAMEMOVE,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.MoveNumber, enumSyntaxSymbol.RESULT },
                        new enumLexToken[] { enumLexToken.MoveNumber },
                        new enumLexToken[] { enumLexToken.Win1, enumLexToken.Draw1, enumLexToken.Loss1,
                                              enumLexToken.Win2, enumLexToken.Draw2, enumLexToken.Loss2,
                                                enumLexToken.DoubleForfeit},
                        true);

            // Special rule for incompatibility of PDN formats of Czech Federation of Checkers
            AddParsingRule(enumSyntaxSymbol.GAMEMOVE,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.MoveNumber, enumSyntaxSymbol.Asterix },
                        new enumLexToken[] { enumLexToken.MoveNumber },
                        new enumLexToken[] { enumLexToken.Asterix },
                        true);

            AddParsingRule(enumSyntaxSymbol.GAMEMOVE,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.GAMEMOVE_C },
                        new enumLexToken[] {  enumLexToken.AlphaSquare, enumLexToken.NumSquare },
                        new enumLexToken[] {},
                        true);
            // GAMEMOVE_C 
            AddParsingRule(enumSyntaxSymbol.GAMEMOVE_C,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.NORMALMOVE, enumSyntaxSymbol.GAMEMOVE_C2 },
                        new enumLexToken[] { enumLexToken.AlphaSquare, enumLexToken.NumSquare},
                        new enumLexToken[] { enumLexToken.MoveSeparator},
                        false);
            AddParsingRule(enumSyntaxSymbol.GAMEMOVE_C,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.CAPTUREMOVE, enumSyntaxSymbol.GAMEMOVE_C2 },
                        new enumLexToken[] { enumLexToken.AlphaSquare, enumLexToken.NumSquare },
                        new enumLexToken[] { enumLexToken.CaptureSeparator },
                        false);

            AddParsingRule(enumSyntaxSymbol.GAMEMOVE_C,
                          new enumSyntaxSymbol[] { enumSyntaxSymbol.Ellipses },
                          new enumLexToken[] { enumLexToken.Ellipses },
                          new enumLexToken[] { },
                          false);

            // GAMEMOVE_C2 
            AddParsingRule(enumSyntaxSymbol.GAMEMOVE_C2,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.MoveStrength },
                        new enumLexToken[] { enumLexToken.MoveStrength },
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.GAMEMOVE_C2,
                        new enumSyntaxSymbol[] {},
                        new enumLexToken[] { enumLexToken.OtherSymbol },
                        new enumLexToken[] {},
                        false);
            // NORMALMOVE 
            AddParsingRule(enumSyntaxSymbol.NORMALMOVE,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.POSITION, enumSyntaxSymbol.MoveSeparator, enumSyntaxSymbol.POSITION },
                        new enumLexToken[] { enumLexToken.AlphaSquare, enumLexToken.NumSquare },
                        new enumLexToken[] {},
                        false);
            // CAPTUREMOVE 
            AddParsingRule(enumSyntaxSymbol.CAPTUREMOVE,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.POSITION, enumSyntaxSymbol.CAPTUREMOVE_C},
                        new enumLexToken[] { enumLexToken.AlphaSquare, enumLexToken.NumSquare },
                        new enumLexToken[] {},
                        false);
            // CAPTUREMOVE_C
            AddParsingRule(enumSyntaxSymbol.CAPTUREMOVE_C,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.CaptureSeparator, enumSyntaxSymbol.POSITION, enumSyntaxSymbol.CAPTUREMOVE_C },
                        new enumLexToken[] { enumLexToken.CaptureSeparator },
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.CAPTUREMOVE_C,
                        new enumSyntaxSymbol[] { },
                        new enumLexToken[] { enumLexToken.OtherSymbol },
                        new enumLexToken[] {},
                        false);
            // POSITION 
            AddParsingRule(enumSyntaxSymbol.POSITION,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.AlphaSquare },
                        new enumLexToken[] { enumLexToken.AlphaSquare},
                        new enumLexToken[] {},
                        false);
            AddParsingRule(enumSyntaxSymbol.POSITION,
                        new enumSyntaxSymbol[] { enumSyntaxSymbol.NumSquare },
                        new enumLexToken[] { enumLexToken.NumSquare },
                        new enumLexToken[] { },
                        false);

        }
        
        /// <summary>
        /// Creates the rule record
        /// </summary>
        private void AddParsingRule(enumSyntaxSymbol startToken, enumSyntaxSymbol[] rule, enumLexToken[] firstCatchSymbol,
                        enumLexToken[] secondCatchSymbol, bool popStack)
        {
            PDNParserRule pdnRule = new PDNParserRule();
            pdnRule.StartingToken = startToken;
            pdnRule.Rule = rule.ToList();
            pdnRule.FirstCatchSymbols = firstCatchSymbol.ToList();
            pdnRule.SecondCatchSymbols = secondCatchSymbol.ToList();
            pdnRule.PopStack = popStack;

            lstRules.Add(pdnRule);
        }

        #endregion

        #region Class for streaming lexical tokens

        /// <summary>
        /// Class for lexical stream
        /// </summary>
        private class LexicalStream
        {
            private List<LexToken> lstLexTokens;
            private long _lIndex;

            /// <summary>
            /// Init the stream
            /// </summary>
            public LexicalStream(List<LexToken> list)
            {
                lstLexTokens = new List<LexToken>();
                lstLexTokens.AddRange(list);
                _lIndex = 0;
            }

            /// <summary>
            /// Returns the EOF of the stream
            /// </summary>
            public bool EOF
            {
                get
                {
                    return _lIndex >= lstLexTokens.Count;
                }
            }

            /// <summary>
            /// Peeks the stream for the next tokens
            /// </summary>
            public List<enumLexToken> PeekStream(int lCount = 2)
            {
                List<enumLexToken> lstOutput = new List<enumLexToken>();

                long lTokenNumber = 0;

                for (long i =_lIndex; i < lstLexTokens.Count; i++)
                {
                    if (lTokenNumber >= lCount)
                        break;
                    lstOutput.Add(lstLexTokens[(int)i].LexTokenType);
                    
                    lTokenNumber++;                                                                        
                }

                while (lTokenNumber < lCount)
                {
                    lTokenNumber++;
                    lstOutput.Add(enumLexToken.EOF);
                }

                return lstOutput;
            }

            /// <summary>
            /// Reads the one token
            /// </summary>
            public LexToken Read()
            {
                // if (_lIndex >= lstLexTokens.Count)
                    //throw new ApplicationException("LexicalStream is EOF");
                return lstLexTokens[(int)_lIndex++];
            }
        }

        #endregion

    }
}
